/*
* Kinect Video driver - This code emulates a real video device with v4l2 api
* and libfreenect
*
* Copyright (c) 2006 by:
*      Mauro Carvalho Chehab <mchehab--a.t--infradead.org>
*      Ted Walther <ted--a.t--enumera.com>
*      John Sokol <sokol--a.t--videotechnology.com>
*      http://v4l.videotechnology.com/
* Copyright (c) 2010 Modifications for Kinect by:
*      Tisham Dhar <whatnickd--a.t.--gmail.com>
*      
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the BSD Licence, GNU General Public License
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version
*/

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/pci.h>
#include <linux/random.h>
#include <linux/version.h>
#include <linux/mutex.h>
#include <linux/videodev2.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/kthread.h>
#include <linux/highmem.h>
#include <linux/freezer.h>
#include <media/videobuf-vmalloc.h>
#include <media/v4l2-device.h>
#include <media/v4l2-ioctl.h>
#include "font.h"
 
#define KINECT_MODULE_NAME "kinect"

/* Wake up at about 30 fps */
#define WAKE_NUMERATOR 30
#define WAKE_DENOMINATOR 1001
#define BUFFER_TIMEOUT     msecs_to_jiffies(500)  /* 0.5 seconds */

#define KINECT_MAJOR_VERSION 0
#define KINECT_MINOR_VERSION 1
#define KINECT_RELEASE 0
#define KINECT_VERSION \
        KERNEL_VERSION(KINECT_MAJOR_VERSION, KINECT_MINOR_VERSION, KINECT_RELEASE)

MODULE_DESCRIPTION("Microsoft Kinect RGB-Depth Sensor");
MODULE_AUTHOR("Tisham Dhar");
MODULE_LICENSE("GPL");

static unsigned video_nr = -1;
module_param(video_nr, uint, 0644);
MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");

static unsigned n_devs = 2;
module_param(n_devs, uint, 0644);
MODULE_PARM_DESC(n_devs, "number of video devices to create");
 
static unsigned debug;
module_param(debug, uint, 0644);
MODULE_PARM_DESC(debug, "activates debug info");
 
static unsigned int vid_limit = 16;
module_param(vid_limit, uint, 0644);
MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
 
 
/* supported controls */
static struct v4l2_queryctrl kinect_qctrl[] = {
        {
                 .id            = V4L2_CID_AUDIO_VOLUME,
                .name          = "Volume",
                .minimum       = 0,
                .maximum       = 65535,
                .step          = 65535/100,
                .default_value = 65535,
                .flags         = V4L2_CTRL_FLAG_SLIDER,
                .type          = V4L2_CTRL_TYPE_INTEGER,
         }, {
                 .id            = V4L2_CID_BRIGHTNESS,
                 .type          = V4L2_CTRL_TYPE_INTEGER,
                 .name          = "Brightness",
                 .minimum       = 0,
                 .maximum       = 255,
                 .step          = 1,
                 .default_value = 127,
                 .flags         = V4L2_CTRL_FLAG_SLIDER,
         }, {
                 .id            = V4L2_CID_CONTRAST,
                 .type          = V4L2_CTRL_TYPE_INTEGER,
                 .name          = "Contrast",
                 .minimum       = 0,
                 .maximum       = 255,
                 .step          = 0x1,
                 .default_value = 0x10,
                 .flags         = V4L2_CTRL_FLAG_SLIDER,
         }, {
                 .id            = V4L2_CID_SATURATION,
                 .type          = V4L2_CTRL_TYPE_INTEGER,
                 .name          = "Saturation",
                 .minimum       = 0,
                 .maximum       = 255,
                 .step          = 0x1,
                 .default_value = 127,
                 .flags         = V4L2_CTRL_FLAG_SLIDER,
         }, {
                 .id            = V4L2_CID_HUE,
                 .type          = V4L2_CTRL_TYPE_INTEGER,
                 .name          = "Hue",
                 .minimum       = -128,
                 .maximum       = 127,
                 .step          = 0x1,
                 .default_value = 0,
                 .flags         = V4L2_CTRL_FLAG_SLIDER,
         }
 };
 
#define dprintk(dev, level, fmt, arg...) \
         v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)

/*------------------------------------------------------------------
         Basic structures
------------------------------------------------------------------*/

struct kinect_fmt {
        char  *name;
        u32   fourcc;          /* v4l2 format id */
        int   depth;
 };

static struct kinect_fmt formats[] = {
        {
                .name     = "4:2:2, packed, YUYV",
                .fourcc   = V4L2_PIX_FMT_YUYV,
                .depth    = 16,
         },
        {
                .name     = "4:2:2, packed, UYVY",
                .fourcc   = V4L2_PIX_FMT_UYVY,
                .depth    = 16,
        },
        {
                .name     = "RGB565 (LE)",
                .fourcc   = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */
                .depth    = 16,
        },
        {
                .name     = "RGB565 (BE)",
                .fourcc   = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
                .depth    = 16,
        },
        {
                .name     = "RGB555 (LE)",
                .fourcc   = V4L2_PIX_FMT_RGB555, /* gggbbbbb arrrrrgg */
                .depth    = 16,
        },
        {
                .name     = "RGB555 (BE)",
                .fourcc   = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */
                .depth    = 16,
        },
};

static struct kinect_fmt *get_format(struct v4l2_format *f)
{
        struct kinect_fmt *fmt;
        unsigned int k;

        for (k = 0; k < ARRAY_SIZE(formats); k++) {
                fmt = &formats[k];
                if (fmt->fourcc == f->fmt.pix.pixelformat)
                        break;
        }

        if (k == ARRAY_SIZE(formats))
                return NULL;

        return &formats[k];
}

struct sg_to_addr {
        int pos;
        struct scatterlist *sg;
};

/* buffer for one video frame */
struct kinect_buffer {
        /* common v4l buffer stuff -- must be first */
        struct videobuf_buffer vb;

        struct kinect_fmt        *fmt;
};

struct kinect_dmaqueue {
        struct list_head       active;

        /* thread for generating video stream*/
        struct task_struct         *kthread;
        wait_queue_head_t          wq;
        /* Counters to control fps rate */
        int                        frame;
        int                        ini_jiffies;
};

static LIST_HEAD(kinect_devlist);

struct kinect_dev {
        struct list_head           kinect_devlist;
        struct v4l2_device         v4l2_dev;

        spinlock_t                 slock;
        struct mutex               mutex;

        int                        users;

        /* various device info */
        struct video_device        *vfd;

        struct kinect_dmaqueue       vidq;

        /* Several counters */
        int                        h, m, s, ms;
        unsigned long              jiffies;
        char                       timestr[13];

        int                        mv_count;    /* Controls bars movement */

        /* Input Number */
        int                        input;

        /* Control 'registers' */
        int                        qctl_regs[ARRAY_SIZE(kinect_qctrl)];
};

struct kinect_fh {
        struct kinect_dev            *dev;

        /* video capture */
        struct kinect_fmt            *fmt;
        unsigned int               width, height;
        struct videobuf_queue      vb_vidq;

        enum v4l2_buf_type         type;
        unsigned char              bars[8][3];
        int                        input;       /*TODO: Clean out as needed */
};

/* ------------------------------------------------------------------
        DMA and thread functions
   ------------------------------------------------------------------*/

/*TODO: Clean Bars and Colors should match positions */

enum colors {
        WHITE,
        AMBAR,
        CYAN,
        GREEN,
        MAGENTA,
        RED,
        BLUE,
        BLACK,
};
 
        /* R   G   B */
#define COLOR_WHITE     {204, 204, 204}
#define COLOR_AMBAR     {208, 208,   0}
#define COLOR_CIAN      {  0, 206, 206}
#define COLOR_GREEN     {  0, 239,   0}
#define COLOR_MAGENTA   {239,   0, 239}
#define COLOR_RED       {205,   0,   0}
#define COLOR_BLUE      {  0,   0, 255}
#define COLOR_BLACK     {  0,   0,   0}

struct bar_std {
        u8 bar[8][3];
};

/* Maximum number of bars are 10 - otherwise, the input print code
   should be modified */
static struct bar_std bars[] = {
        {       /* Standard ITU-R color bar sequence */
                {
                        COLOR_WHITE,
                        COLOR_AMBAR,
                        COLOR_CIAN,
                        COLOR_GREEN,
                        COLOR_MAGENTA,
                        COLOR_RED,
                        COLOR_BLUE,
                        COLOR_BLACK,
                }
        }, {
                {
                        COLOR_WHITE,
                        COLOR_AMBAR,
                        COLOR_BLACK,
                        COLOR_WHITE,
                        COLOR_AMBAR,
                        COLOR_BLACK,
                        COLOR_WHITE,
                        COLOR_AMBAR,
                }
        }, {
                {
                        COLOR_WHITE,
                        COLOR_CIAN,
                        COLOR_BLACK,
                        COLOR_WHITE,
                        COLOR_CIAN,
                        COLOR_BLACK,
                        COLOR_WHITE,
                        COLOR_CIAN,
                }
        }, {
                {
                        COLOR_WHITE,
                        COLOR_GREEN,
                        COLOR_BLACK,
                        COLOR_WHITE,
                        COLOR_GREEN,
                        COLOR_BLACK,
                        COLOR_WHITE,
                        COLOR_GREEN,
                }
        },
};

#define NUM_INPUTS ARRAY_SIZE(bars)

#define TO_Y(r, g, b) \
        (((16829 * r + 33039 * g + 6416 * b  + 32768) >> 16) + 16)
/* RGB to  V(Cr) Color transform */
#define TO_V(r, g, b) \
        (((28784 * r - 24103 * g - 4681 * b  + 32768) >> 16) + 128)
/* RGB to  U(Cb) Color transform */
#define TO_U(r, g, b) \
        (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)

/* precalculate color bar values to speed up rendering */
static void precalculate_bars(struct kinect_fh *fh)
{
        struct kinect_dev *dev = fh->dev;
        unsigned char r, g, b;
        int k, is_yuv;

        fh->input = dev->input;

        for (k = 0; k < 8; k++) {
                r = bars[fh->input].bar[k][0];
                g = bars[fh->input].bar[k][1];
                b = bars[fh->input].bar[k][2];
                is_yuv = 0;

                switch (fh->fmt->fourcc) {
                case V4L2_PIX_FMT_YUYV:
                case V4L2_PIX_FMT_UYVY:
                        is_yuv = 1;
                        break;
                case V4L2_PIX_FMT_RGB565:
                case V4L2_PIX_FMT_RGB565X:
                        r >>= 3;
                        g >>= 2;
                        b >>= 3;
                        break;
                case V4L2_PIX_FMT_RGB555:
                case V4L2_PIX_FMT_RGB555X:
                        r >>= 3;
                        g >>= 3;
                        b >>= 3;
                        break;
                }

                if (is_yuv) {
                        fh->bars[k][0] = TO_Y(r, g, b); /* Luma */
                        fh->bars[k][1] = TO_U(r, g, b); /* Cb */
                        fh->bars[k][2] = TO_V(r, g, b); /* Cr */
                } else {
                        fh->bars[k][0] = r;
                        fh->bars[k][1] = g;
                        fh->bars[k][2] = b;
                }
        }

}

#define TSTAMP_MIN_Y    24
#define TSTAMP_MAX_Y    (TSTAMP_MIN_Y + 15)
#define TSTAMP_INPUT_X  10
#define TSTAMP_MIN_X    (54 + TSTAMP_INPUT_X)

static void gen_twopix(struct kinect_fh *fh, unsigned char *buf, int colorpos)
{
        unsigned char r_y, g_u, b_v;
        unsigned char *p;
        int color;

        r_y = fh->bars[colorpos][0]; /* R or precalculated Y */
        g_u = fh->bars[colorpos][1]; /* G or precalculated U */
        b_v = fh->bars[colorpos][2]; /* B or precalculated V */

        for (color = 0; color < 4; color++) {
                p = buf + color;

                switch (fh->fmt->fourcc) {
                case V4L2_PIX_FMT_YUYV:
                         switch (color) {
                        case 0:
                        case 2:
                                *p = r_y;
                                 break;
                        case 1:
                                *p = g_u;
                                break;
                        case 3:
                                *p = b_v;
                                break;
                        }
                        break;
                case V4L2_PIX_FMT_UYVY:
                        switch (color) {
                        case 1:
                        case 3:
                                *p = r_y;
                                break;
                        case 0:
                                *p = g_u;
                                break;
                        case 2:
                                *p = b_v;
                                break;
                        }
                        break;
                case V4L2_PIX_FMT_RGB565:
                        switch (color) {
                        case 0:
                        case 2:
                                *p = (g_u << 5) | b_v;
                                break;
                        case 1:
                        case 3:
                                *p = (r_y << 3) | (g_u >> 3);
                                break;
                        }
                        break;
                case V4L2_PIX_FMT_RGB565X:
                        switch (color) {
                        case 0:
                        case 2:
                                *p = (r_y << 3) | (g_u >> 3);
                                break;
                        case 1:
                        case 3:
                                *p = (g_u << 5) | b_v;
                                break;
                        }
                        break;
                case V4L2_PIX_FMT_RGB555:
                        switch (color) {
                        case 0:
                        case 2:
                                *p = (g_u << 5) | b_v;
                                break;
                        case 1:
                        case 3:
                                *p = (r_y << 2) | (g_u >> 3);
                                break;
                        }
                        break;
                case V4L2_PIX_FMT_RGB555X:
                        switch (color) {
                        case 0:
                        case 2:
                                *p = (r_y << 2) | (g_u >> 3);
                                break;
                        case 1:
                        case 3:
                                *p = (g_u << 5) | b_v;
                                break;
                        }
                        break;
                 }
        }
}

static void gen_line(struct kinect_fh *fh, char *basep, int inipos, int wmax,
                int hmax, int line, int count, char *timestr)
{
        int  w, i, j;
        int pos = inipos;
        char *s;
        u8 chr;

        /* We will just duplicate the second pixel at the packet */
        wmax /= 2;

        /* Generate a standard color bar pattern */
        for (w = 0; w < wmax; w++) {
                int colorpos = ((w + count) * 8/(wmax + 1)) % 8;

                gen_twopix(fh, basep + pos, colorpos);
                pos += 4; /* only 16 bpp supported for now */
        }

        /* Prints input entry number */

        /* Checks if it is possible to input number */
        if (TSTAMP_MAX_Y >= hmax)
                goto end;

        if (TSTAMP_INPUT_X + strlen(timestr) >= wmax)
                goto end;

        if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) {
                chr = rom8x16_bits[fh->input * 16 + line - TSTAMP_MIN_Y];
                pos = TSTAMP_INPUT_X;
                for (i = 0; i < 7; i++) {
                        /* Draw white font on black background */
                        if (chr & 1 << (7 - i))
                                gen_twopix(fh, basep + pos, WHITE);
                        else
                                gen_twopix(fh, basep + pos, BLACK);
                        pos += 2;
                }
        }

        /* Checks if it is possible to show timestamp */
        if (TSTAMP_MIN_X + strlen(timestr) >= wmax)
                goto end;

        /* Print stream time */
        if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) {
                j = TSTAMP_MIN_X;
                for (s = timestr; *s; s++) {
                        chr = rom8x16_bits[(*s-0x30)*16+line-TSTAMP_MIN_Y];
                        for (i = 0; i < 7; i++) {
                                pos = inipos + j * 2;
                                /* Draw white font on black background */
                                if (chr & 1 << (7 - i))
                                        gen_twopix(fh, basep + pos, WHITE);
                                else
                                        gen_twopix(fh, basep + pos, BLACK);
                                j++;
                        }
                }
        }

end:
        return;
}

static void kinect_fillbuff(struct kinect_fh *fh, struct kinect_buffer *buf)
{
        struct kinect_dev *dev = fh->dev;
        int h , pos = 0;
        int hmax  = buf->vb.height;
        int wmax  = buf->vb.width;
        struct timeval ts;
        char *tmpbuf;
        void *vbuf = videobuf_to_vmalloc(&buf->vb);

        if (!vbuf)
                return;

        tmpbuf = kmalloc(wmax * 2, GFP_ATOMIC);
        if (!tmpbuf)
                return;

        for (h = 0; h < hmax; h++) {
                gen_line(fh, tmpbuf, 0, wmax, hmax, h, dev->mv_count,
                         dev->timestr);
                memcpy(vbuf + pos, tmpbuf, wmax * 2);
                pos += wmax*2;
        }

        dev->mv_count++;

        kfree(tmpbuf);

        /* Updates stream time */

        dev->ms += jiffies_to_msecs(jiffies-dev->jiffies);
        dev->jiffies = jiffies;
        if (dev->ms >= 1000) {
                dev->ms -= 1000;
                dev->s++;
                if (dev->s >= 60) {
                        dev->s -= 60;
                        dev->m++;
                        if (dev->m > 60) {
                                dev->m -= 60;
                                dev->h++;
                                if (dev->h > 24)
                                        dev->h -= 24;
                        }
                }
        }
        sprintf(dev->timestr, "%02d:%02d:%02d:%03d",
                        dev->h, dev->m, dev->s, dev->ms);

        dprintk(dev, 2, "knectfill at %s: Buffer 0x%08lx size= %d\n",
                        dev->timestr, (unsigned long)tmpbuf, pos);

        /* Advice that buffer was filled */
        buf->vb.field_count++;
        do_gettimeofday(&ts);
        buf->vb.ts = ts;
        buf->vb.state = VIDEOBUF_DONE;
}

static void kinect_thread_tick(struct kinect_fh *fh)
{
        struct kinect_buffer *buf;
        struct kinect_dev *dev = fh->dev;
        struct kinect_dmaqueue *dma_q = &dev->vidq;

        unsigned long flags = 0;

        dprintk(dev, 1, "Thread tick\n");

        spin_lock_irqsave(&dev->slock, flags);
        if (list_empty(&dma_q->active)) {
                dprintk(dev, 1, "No active queue to serve\n");
                goto unlock;
        }

        buf = list_entry(dma_q->active.next,
                         struct kinect_buffer, vb.queue);

        /* Nobody is waiting on this buffer, return */
        if (!waitqueue_active(&buf->vb.done))
                goto unlock;

        list_del(&buf->vb.queue);

        do_gettimeofday(&buf->vb.ts);

        /* Fill buffer */
        kinect_fillbuff(fh, buf);
        dprintk(dev, 1, "filled buffer %p\n", buf);

        wake_up(&buf->vb.done);
        dprintk(dev, 2, "[%p/%d] wakeup\n", buf, buf->vb. i);
unlock:
        spin_unlock_irqrestore(&dev->slock, flags);
        return;
}

#define frames_to_ms(frames)                                    \
        ((frames * WAKE_NUMERATOR * 1000) / WAKE_DENOMINATOR)

static void kinect_sleep(struct kinect_fh *fh)
{
        struct kinect_dev *dev = fh->dev;
        struct kinect_dmaqueue *dma_q = &dev->vidq;
        int timeout;
        DECLARE_WAITQUEUE(wait, current);

        dprintk(dev, 1, "%s dma_q=0x%08lx\n", __func__,
                (unsigned long)dma_q);

        add_wait_queue(&dma_q->wq, &wait);
        if (kthread_should_stop())
                goto stop_task;

        /* Calculate time to wake up */
        timeout = msecs_to_jiffies(frames_to_ms(1));

        kinect_thread_tick(fh);

        schedule_timeout_interruptible(timeout);

stop_task:
        remove_wait_queue(&dma_q->wq, &wait);
        try_to_freeze();
}

static int kinect_thread(void *data)
{
        struct kinect_fh  *fh = data;
        struct kinect_dev *dev = fh->dev;

        dprintk(dev, 1, "thread started\n");

        set_freezable();

        for (;;) {
                kinect_sleep(fh);

                if (kthread_should_stop())
                        break;
        }
        dprintk(dev, 1, "thread: exit\n");
        return 0;
}

static int kinect_start_thread(struct kinect_fh *fh)
{
        struct kinect_dev *dev = fh->dev;
        struct kinect_dmaqueue *dma_q = &dev->vidq;

        dma_q->frame = 0;
        dma_q->ini_jiffies = jiffies;

        dprintk(dev, 1, "%s\n", __func__);

        dma_q->kthread = kthread_run(kinect_thread, fh, "kinect");

        if (IS_ERR(dma_q->kthread)) {
                v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
                return PTR_ERR(dma_q->kthread);
        }
        /* Wakes thread */
        wake_up_interruptible(&dma_q->wq);

        dprintk(dev, 1, "returning from %s\n", __func__);
        return 0;
}

static void kinect_stop_thread(struct kinect_dmaqueue  *dma_q)
{
        struct kinect_dev *dev = container_of(dma_q, struct kinect_dev, vidq);

        dprintk(dev, 1, "%s\n", __func__);
        /* shutdown control thread */
        if (dma_q->kthread) {
                kthread_stop(dma_q->kthread);
                dma_q->kthread = NULL;
        }
}

/* ------------------------------------------------------------------
        Videobuf operations
   ------------------------------------------------------------------*/
static int
buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
{
        struct kinect_fh  *fh = vq->priv_data;
        struct kinect_dev *dev  = fh->dev;

        *size = fh->width*fh->height*2;

        if (0 == *count)
                *count = 32;

        while (*size * *count > vid_limit * 1024 * 1024)
                (*count)--;

        dprintk(dev, 1, "%s, count=%d, size=%d\n", __func__,
                *count, *size);

        return 0;
}

static void free_buffer(struct videobuf_queue *vq, struct kinect_buffer *buf)
{
        struct kinect_fh  *fh = vq->priv_data;
        struct kinect_dev *dev  = fh->dev;

        dprintk(dev, 1, "%s, state: %i\n", __func__, buf->vb.state);

        if (in_interrupt())
                BUG();

        videobuf_vmalloc_free(&buf->vb);
        dprintk(dev, 1, "free_buffer: freed\n");
        buf->vb.state = VIDEOBUF_NEEDS_INIT;
}

#define norm_maxw() 640
#define norm_maxh() 480
static int
buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
                                                enum v4l2_field field)
{
        struct kinect_fh     *fh  = vq->priv_data;
        struct kinect_dev    *dev = fh->dev;
        struct kinect_buffer *buf = container_of(vb, struct kinect_buffer, vb);
        int rc;

        dprintk(dev, 1, "%s, field=%d\n", __func__, field);

        BUG_ON(NULL == fh->fmt);

        if (fh->width  < 48 || fh->width  > norm_maxw() ||
            fh->height < 32 || fh->height > norm_maxh())
                return -EINVAL;

        buf->vb.size = fh->width*fh->height*2;
        if (0 != buf->vb.baddr  &&  buf->vb.bsize < buf->vb.size)
                return -EINVAL;

        /* These properties only change when queue is idle, see s_fmt */
        buf->fmt       = fh->fmt;
        buf->vb.width  = fh->width;
        buf->vb.height = fh->height;
        buf->vb.field  = field;

        precalculate_bars(fh);

        if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
                rc = videobuf_iolock(vq, &buf->vb, NULL);
                if (rc < 0)
                        goto fail;
        }

        buf->vb.state = VIDEOBUF_PREPARED;

        return 0;

fail:
        free_buffer(vq, buf);
        return rc;
}
 
static void
buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
{
        struct kinect_buffer    *buf  = container_of(vb, struct kinect_buffer, vb);
        struct kinect_fh        *fh   = vq->priv_data;
        struct kinect_dev       *dev  = fh->dev;
        struct kinect_dmaqueue *vidq = &dev->vidq;

        dprintk(dev, 1, "%s\n", __func__);

        buf->vb.state = VIDEOBUF_QUEUED;
        list_add_tail(&buf->vb.queue, &vidq->active);
}

static void buffer_release(struct videobuf_queue *vq,
                           struct videobuf_buffer *vb)
{
        struct kinect_buffer   *buf  = container_of(vb, struct kinect_buffer, vb);
        struct kinect_fh       *fh   = vq->priv_data;
        struct kinect_dev      *dev  = (struct kinect_dev *)fh->dev;

        dprintk(dev, 1, "%s\n", __func__);

        free_buffer(vq, buf);
}

static struct videobuf_queue_ops kinect_video_qops = {
        .buf_setup      = buffer_setup,
        .buf_prepare    = buffer_prepare,
        .buf_queue      = buffer_queue,
        .buf_release    = buffer_release,
};

/* ------------------------------------------------------------------
        IOCTL vidioc handling
   ------------------------------------------------------------------*/
static int vidioc_querycap(struct file *file, void  *priv,
                                        struct v4l2_capability *cap)
{
        struct kinect_fh  *fh  = priv;
        struct kinect_dev *dev = fh->dev;

        strcpy(cap->driver, "kinect");
        strcpy(cap->card, "kinect");
        strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
        cap->version = KINECT_VERSION;
        cap->capabilities =     V4L2_CAP_VIDEO_CAPTURE |
                                V4L2_CAP_STREAMING     |
                                V4L2_CAP_READWRITE;
        return 0;
}

static int vidioc_enum_fmt_vid_cap(struct file *file, void  *priv,
                                        struct v4l2_fmtdesc *f)
{
        struct kinect_fmt *fmt;

        if (f->index >= ARRAY_SIZE(formats))
                return -EINVAL;

        fmt = &formats[f->index];

        strlcpy(f->description, fmt->name, sizeof(f->description));
        f->pixelformat = fmt->fourcc;
        return 0;
}
 
static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
{
        struct kinect_fh *fh = priv;

        f->fmt.pix.width        = fh->width;
        f->fmt.pix.height       = fh->height;
        f->fmt.pix.field        = fh->vb_vidq.field;
        f->fmt.pix.pixelformat  = fh->fmt->fourcc;
        f->fmt.pix.bytesperline =
                (f->fmt.pix.width * fh->fmt->depth) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;

        return (0);
}

/* TODO: Support 16bit depth and 8bit RGB */
static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
                        struct v4l2_format *f)
{
        struct kinect_fh  *fh  = priv;
        struct kinect_dev *dev = fh->dev;
        struct kinect_fmt *fmt;
        enum v4l2_field field;
        unsigned int maxw, maxh;

        fmt = get_format(f);
        if (!fmt) {
                dprintk(dev, 1, "Fourcc format (0x%08x) invalid.\n",
                        f->fmt.pix.pixelformat);
                return -EINVAL;
        }

        field = f->fmt.pix.field;

        if (field == V4L2_FIELD_ANY) {
                field = V4L2_FIELD_INTERLACED;
        } else if (V4L2_FIELD_INTERLACED != field) {
                dprintk(dev, 1, "Field type invalid.\n");
                return -EINVAL;
        }

        maxw  = norm_maxw();
        maxh  = norm_maxh();

        f->fmt.pix.field = field;
        v4l_bound_align_image(&f->fmt.pix.width, 48, maxw, 2,
                              &f->fmt.pix.height, 32, maxh, 0, 0);
        f->fmt.pix.bytesperline =
                (f->fmt.pix.width * fmt->depth) >> 3;
        f->fmt.pix.sizeimage =
                f->fmt.pix.height * f->fmt.pix.bytesperline;

        return 0;
}

/*FIXME: This seems to be generic enough to be at videodev2 */
static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
                                        struct v4l2_format *f)
{
        struct kinect_fh *fh = priv;
        struct videobuf_queue *q = &fh->vb_vidq;

        int ret = vidioc_try_fmt_vid_cap(file, fh, f);
        if (ret < 0)
                return ret;

        mutex_lock(&q->vb_lock);

        if (videobuf_queue_is_busy(&fh->vb_vidq)) {
                dprintk(fh->dev, 1, "%s queue busy\n", __func__);
                ret = -EBUSY;
                goto out;
        }

        fh->fmt           = get_format(f);
        fh->width         = f->fmt.pix.width;
        fh->height        = f->fmt.pix.height;
        fh->vb_vidq.field = f->fmt.pix.field;
        fh->type          = f->type;

        ret = 0;
out:
        mutex_unlock(&q->vb_lock);

        return ret;
}

static int vidioc_reqbufs(struct file *file, void *priv,
                          struct v4l2_requestbuffers *p)
{
        struct kinect_fh  *fh = priv;

        return (videobuf_reqbufs(&fh->vb_vidq, p));
}

static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
        struct kinect_fh  *fh = priv;

        return (videobuf_querybuf(&fh->vb_vidq, p));
}
 
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
        struct kinect_fh *fh = priv;

        return (videobuf_qbuf(&fh->vb_vidq, p));
}

static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p)
{
        struct kinect_fh  *fh = priv;

        return (videobuf_dqbuf(&fh->vb_vidq, p,
                                file->f_flags & O_NONBLOCK));
}

#ifdef CONFIG_VIDEO_V4L1_COMPAT
static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
{
        struct kinect_fh  *fh = priv;

        return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
}
#endif

static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
{
        struct kinect_fh  *fh = priv;

        if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
        if (i != fh->type)
                return -EINVAL;

        return videobuf_streamon(&fh->vb_vidq);
}

static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i)
{
        struct kinect_fh  *fh = priv;

        if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
                return -EINVAL;
        if (i != fh->type)
                return -EINVAL;

        return videobuf_streamoff(&fh->vb_vidq);
}
 
static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
{
        return 0;
}
 
/* only one input in this sample driver */
/* TODO: 2 inputs in the not so simple Kinect driver */
static int vidioc_enum_input(struct file *file, void *priv,
                                struct v4l2_input *inp)
{
        if (inp->index >= NUM_INPUTS)
                return -EINVAL;

        inp->type = V4L2_INPUT_TYPE_CAMERA;
        inp->std = V4L2_STD_525_60;
        sprintf(inp->name, "Camera %u", inp->index);

        return (0);
}

static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
{
        struct kinect_fh *fh = priv;
        struct kinect_dev *dev = fh->dev;

        *i = dev->input;

        return (0);
}
static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
{
        struct kinect_fh *fh = priv;
        struct kinect_dev *dev = fh->dev;

        if (i >= NUM_INPUTS)
                return -EINVAL;

        dev->input = i;
        precalculate_bars(fh);

        return (0);
}

        /* --- controls ---------------------------------------------- */
static int vidioc_queryctrl(struct file *file, void *priv,
                            struct v4l2_queryctrl *qc)
{
        int i;

        for (i = 0; i < ARRAY_SIZE(kinect_qctrl); i++)
                if (qc->id && qc->id == kinect_qctrl[i].id) {
                        memcpy(qc, &(kinect_qctrl[i]),
                                sizeof(*qc));
                        return (0);
                }

        return -EINVAL;
}

static int vidioc_g_ctrl(struct file *file, void *priv,
                         struct v4l2_control *ctrl)
{
        struct kinect_fh *fh = priv;
        struct kinect_dev *dev = fh->dev;
        int i;

        for (i = 0; i < ARRAY_SIZE(kinect_qctrl); i++)
                if (ctrl->id == kinect_qctrl[i].id) {
                        ctrl->value = dev->qctl_regs[i];
                        return 0;
                }

        return -EINVAL;
}
static int vidioc_s_ctrl(struct file *file, void *priv,
                                struct v4l2_control *ctrl)
{
        struct kinect_fh *fh = priv;
        struct kinect_dev *dev = fh->dev;
        int i;

        for (i = 0; i < ARRAY_SIZE(kinect_qctrl); i++)
                if (ctrl->id == kinect_qctrl[i].id) {
                        if (ctrl->value < kinect_qctrl[i].minimum ||
                            ctrl->value > kinect_qctrl[i].maximum) {
                                return -ERANGE;
                        }
                        dev->qctl_regs[i] = ctrl->value;
                        return 0;
                }
        return -EINVAL;
}

/* ------------------------------------------------------------------
        File operations for the device
   ------------------------------------------------------------------*/

static int kinect_open(struct file *file)
{
        struct kinect_dev *dev = video_drvdata(file);
        struct kinect_fh *fh = NULL;
        int retval = 0;

        mutex_lock(&dev->mutex);
        dev->users++;

        if (dev->users > 1) {
                dev->users--;
                mutex_unlock(&dev->mutex);
                return -EBUSY;
        }

        dprintk(dev, 1, "open /dev/video%d type=%s users=%d\n", dev->vfd->num,
               v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);

        /* allocate + initialize per filehandle data */
        fh = kzalloc(sizeof(*fh), GFP_KERNEL);
        if (NULL == fh) {
                dev->users--;
                retval = -ENOMEM;
        }
        mutex_unlock(&dev->mutex);

        if (retval)
                return retval;

        file->private_data = fh;
        fh->dev      = dev;

        fh->type     = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        fh->fmt      = &formats[0];
        fh->width    = 640;
        fh->height   = 480;

        /* Resets frame counters */
        dev->h = 0;
        dev->m = 0;
        dev->s = 0;
        dev->ms = 0;
        dev->mv_count = 0;
        dev->jiffies = jiffies;
        sprintf(dev->timestr, "%02d:%02d:%02d:%03d",
                        dev->h, dev->m, dev->s, dev->ms);

        videobuf_queue_vmalloc_init(&fh->vb_vidq, &kinect_video_qops,
                        NULL, &dev->slock, fh->type, V4L2_FIELD_INTERLACED,
                        sizeof(struct kinect_buffer), fh);

        kinect_start_thread(fh);

        return 0;
}
 
static ssize_t
kinect_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
        struct kinect_fh *fh = file->private_data;

        if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
                return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0,
                                        file->f_flags & O_NONBLOCK);
        }
        return 0;
}

static unsigned int
kinect_poll(struct file *file, struct poll_table_struct *wait)
{
        struct kinect_fh        *fh = file->private_data;
        struct kinect_dev       *dev = fh->dev;
        struct videobuf_queue *q = &fh->vb_vidq;

        dprintk(dev, 1, "%s\n", __func__);

        if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
                return POLLERR;

        return videobuf_poll_stream(file, q, wait);
}
 
static int kinect_close(struct file *file)
{
        struct kinect_fh         *fh = file->private_data;
        struct kinect_dev *dev       = fh->dev;
        struct kinect_dmaqueue *vidq = &dev->vidq;

        int minor = video_devdata(file)->minor;

        kinect_stop_thread(vidq);
        videobuf_stop(&fh->vb_vidq);
        videobuf_mmap_free(&fh->vb_vidq);

        kfree(fh);

        mutex_lock(&dev->mutex);
        dev->users--;
        mutex_unlock(&dev->mutex);

        dprintk(dev, 1, "close called (minor=%d, users=%d)\n",
                minor, dev->users);

        return 0;
}

static int kinect_mmap(struct file *file, struct vm_area_struct *vma)
{
        struct kinect_fh  *fh = file->private_data;
        struct kinect_dev *dev = fh->dev;
        int ret;

        dprintk(dev, 1, "mmap called, vma=0x%08lx\n", (unsigned long)vma);

        ret = videobuf_mmap_mapper(&fh->vb_vidq, vma);

        dprintk(dev, 1, "vma start=0x%08lx, size=%ld, ret=%d\n",
                (unsigned long)vma->vm_start,
                (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
                ret);

        return ret;
}

static const struct v4l2_file_operations kinect_fops = {
        .owner          = THIS_MODULE,
        .open           = kinect_open,
        .release        = kinect_close,
        .read           = kinect_read,
        .poll           = kinect_poll,
        .ioctl          = video_ioctl2, /* V4L2 ioctl handler */
        .mmap           = kinect_mmap,
};
 
static const struct v4l2_ioctl_ops kinect_ioctl_ops = {
        .vidioc_querycap      = vidioc_querycap,
        .vidioc_enum_fmt_vid_cap  = vidioc_enum_fmt_vid_cap,
        .vidioc_g_fmt_vid_cap     = vidioc_g_fmt_vid_cap,
        .vidioc_try_fmt_vid_cap   = vidioc_try_fmt_vid_cap,
        .vidioc_s_fmt_vid_cap     = vidioc_s_fmt_vid_cap,
        .vidioc_reqbufs       = vidioc_reqbufs,
        .vidioc_querybuf      = vidioc_querybuf,
        .vidioc_qbuf          = vidioc_qbuf,
        .vidioc_dqbuf         = vidioc_dqbuf,
        .vidioc_s_std         = vidioc_s_std,
        .vidioc_enum_input    = vidioc_enum_input,
        .vidioc_g_input       = vidioc_g_input,
        .vidioc_s_input       = vidioc_s_input,
        .vidioc_queryctrl     = vidioc_queryctrl,
        .vidioc_g_ctrl        = vidioc_g_ctrl,
        .vidioc_s_ctrl        = vidioc_s_ctrl,
        .vidioc_streamon      = vidioc_streamon,
        .vidioc_streamoff     = vidioc_streamoff,
#ifdef CONFIG_VIDEO_V4L1_COMPAT
        .vidiocgmbuf          = vidiocgmbuf,
#endif
};

static struct video_device kinect_template = {
        .name           = "kinect",
        .fops           = &kinect_fops,
        .ioctl_ops      = &kinect_ioctl_ops,
        .minor          = -1,
        .release        = video_device_release,

        .tvnorms              = V4L2_STD_525_60,
        .current_norm         = V4L2_STD_NTSC_M,
};
 
/* -----------------------------------------------------------------
        Initialization and module stuff
   ------------------------------------------------------------------*/

static int kinect_release(void)
{
        struct kinect_dev *dev;
        struct list_head *list;

        while (!list_empty(&kinect_devlist)) {
                list = kinect_devlist.next;
                list_del(list);
                dev = list_entry(list, struct kinect_dev, kinect_devlist);

                v4l2_info(&dev->v4l2_dev, "unregistering /dev/video%d\n",
                        dev->vfd->num);
                video_unregister_device(dev->vfd);
                v4l2_device_unregister(&dev->v4l2_dev);
                kfree(dev);
        }

        return 0;
}
 
static int __init kinect_create_instance(int inst)
{
        struct kinect_dev *dev;
        struct video_device *vfd;
        int ret, i;

        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        if (!dev)
                return -ENOMEM;

        snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
                        "%s-%03d", KINECT_MODULE_NAME, inst);
        ret = v4l2_device_register(NULL, &dev->v4l2_dev);
        if (ret)
                goto free_dev;

        /* init video dma queues */
        INIT_LIST_HEAD(&dev->vidq.active);
        init_waitqueue_head(&dev->vidq.wq);

        /* initialize locks */
        spin_lock_init(&dev->slock);
        mutex_init(&dev->mutex);

        ret = -ENOMEM;
        vfd = video_device_alloc();
        if (!vfd)
                goto unreg_dev;

        *vfd = kinect_template;
        vfd->debug = debug;

        ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
        if (ret < 0)
                goto rel_vdev;

        video_set_drvdata(vfd, dev);

        /* Set all controls to their default value. */
        for (i = 0; i < ARRAY_SIZE(kinect_qctrl); i++)
                dev->qctl_regs[i] = kinect_qctrl[i].default_value;

        /* Now that everything is fine, let's add it to device list */
        list_add_tail(&dev->kinect_devlist, &kinect_devlist);

        snprintf(vfd->name, sizeof(vfd->name), "%s (%i)",
                        kinect_template.name, vfd->num);

        if (video_nr >= 0)
                video_nr++;

        dev->vfd = vfd;
        v4l2_info(&dev->v4l2_dev, "V4L2 device registered as /dev/video%d\n",
                        vfd->num);
         return 0;
 
rel_vdev:
         video_device_release(vfd);
unreg_dev:
        v4l2_device_unregister(&dev->v4l2_dev);
free_dev:
        kfree(dev);
        return ret;
}

/* This routine allocates from 1 to n_devs virtual drivers.

   The real maximum number of virtual drivers will depend on how many drivers
   will succeed. This is limited to the maximum number of devices that
   videodev supports, which is equal to VIDEO_NUM_DEVICES.
*/
static int __init kinect_init(void)
{
        int ret = 0, i;

        if (n_devs <= 0)
                n_devs = 1;

        for (i = 0; i < n_devs; i++) {
                ret = kinect_create_instance(i);
                if (ret) {
                        /* If some instantiations succeeded, keep driver */
                        if (i)
                                ret = 0;
                        break;
                }
        }

        if (ret < 0) {
                printk(KERN_INFO "Error %d while loading kinect driver\n", ret);
                return ret;
        }

        printk(KERN_INFO "Microsoft Kinect RGB-Depth "
                        "Capture Device ver %u.%u.%u successfully loaded.\n",
                        (KINECT_VERSION >> 16) & 0xFF, (KINECT_VERSION >> 8) & 0xFF,
                        KINECT_VERSION & 0xFF);

        /* n_devs will reflect the actual number of allocated devices */
        n_devs = i;

        return ret;
}

static void __exit kinect_exit(void)
{
        kinect_release();
}

module_init(kinect_init);
module_exit(kinect_exit);

